%{
#include "Common.h"
#include "ICode.h"
#include "ParseNode.h"
#include "Variable.h"
#include <stdio.h>
#include <string.h>

int yylex();

struct Function* currentFunction;
struct FunctionTable* functionTable;
struct SymbolTable* globalSymbolTable;
unsigned int row = 0, col = 0;
char identifierBuff[IDENTIFIER_NAME_MAX_LEN];
unsigned int identifierLen;

void yyerror(const char* msg);
void Count(void);
void Comment(void);
int CheckType(void);

#define YYMAXDEPTH 0x50000
#define YYINITDEPTH 0x100


struct SemanticValueStruct
{
	union
	{
		struct ParseNode* pn;

		struct Symbol* symbol;

		Float f;
		Float32 f32;
		Float64 f64;

		Int s;
		Int16 s16;
		Int32 s32;

		UInt u;
		UInt16 u16;
		UInt32 u32;
	};


	enum VARIABLE_TYPE_SPECIFIER vts;

	UInt pointerDegree;

	BOOL b;

	Int8 name[IDENTIFIER_NAME_MAX_LEN];

	Int8 parametersDefinitionsNames[IDENTIFIER_NAME_MAX_LEN][FUNCTION_PARAMETERS_SIZE];
	struct VariableType parametersDefinitions[FUNCTION_PARAMETERS_SIZE];
	union
	{
		struct ParseNode* parameters[FUNCTION_PARAMETERS_SIZE];
		struct ParseNode* compoundStatement[COMPOUND_STATEMENT_SIZE];
	};
	
	union
	{
		UInt numStatements;
		UInt numParameters;
	};
};


void SetParserFunctionTable(struct FunctionTable* ft)
{ functionTable = ft; }

void SetParserGlobalSymbolTable(struct SymbolTable* gst)
{ globalSymbolTable = gst; }

void SetParserCurrentFunction(struct Function* f)
{ currentFunction = f; }

struct FunctionTable* GetParserFunctionTable()
{ return functionTable; }

#define YYSTYPE struct SemanticValueStruct

static void PrintTokenValue(FILE* file, int type, YYSTYPE val);
#define YYPRINT(FILE, TYPE, VALUE) PrintTokenValue(FILE, TYPE, VALUE)

#ifdef _MSC_VER
#define BISON_PRINT(STRING, ...) printf("BISON: " STRING "\n", __VA_ARGS__)
#define FLEX_PRINT(STRING, ...) printf("FLEX : " STRING "\n", __VA_ARGS__)
#else
#define BISON_PRINT(STRING, ...) printf("BISON: " STRING "\n", ##__VA_ARGS__)
#define FLEX_PRINT(STRING, ...) printf("FLEX : " STRING "\n", ##__VA_ARGS__)
#endif

#define ADD_FUNCTION_DEFINITION_PARAMETER(SEMANTIC_VALUE, SPECIFIER, POINTER_DEGREE, NAME, PARSENODE)\
	SEMANTIC_VALUE.parametersDefinitions[SEMANTIC_VALUE.numParameters].specifier = SPECIFIER;\
	SEMANTIC_VALUE.parametersDefinitions[SEMANTIC_VALUE.numParameters].pointerDegree = POINTER_DEGREE;\
	strcpy(SEMANTIC_VALUE.parametersDefinitionsNames[SEMANTIC_VALUE.numParameters], NAME);\
	SEMANTIC_VALUE.parameters[SEMANTIC_VALUE.numParameters] = PARSENODE;\
	++SEMANTIC_VALUE.numParameters

#define ADD_FUNCTION_CALL_PARAMETER(SEMANTIC_VALUE, PARSENODE)\
	SEMANTIC_VALUE.parameters[SEMANTIC_VALUE.numParameters] = PARSENODE;\
	++SEMANTIC_VALUE.numParameters

#define ADD_TO_COMPOUND_STATEMENT(SEMANTIC_VALUE, PARSENODE)\
	SEMANTIC_VALUE.compoundStatement[SEMANTIC_VALUE.numStatements] = PARSENODE;\
	++SEMANTIC_VALUE.numStatements;

// Procedure for allocating functions:
// 1. Allocate one function always before
// 2. Parsing statementsEnd: Put them in the unnamed function
// 3. End parsing function: Add to symbol table
%}

%token IDENTIFIER INT_CONST FLOAT_CONST STRING_LITERAL SIZEOF
%token PTR_OP INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token AND_OP OR_OP

%token '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LEFT_ASSIGN
%token RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN TYPE_NAME

%token ELLIPSIS
%token TYPEDEF STATIC INLINE
%token BOOL_TKN INT INT8 INT16 INT32 INT64 UINT UINT8 UINT16 UINT32 UINT64 FLOAT32 FLOAT64 FLOAT

%token CONST VOLATILE VOID
%token CLASS UNION ENUM

%token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN

%start translation_unit
%%

translation_unit
	: external_definition
	| translation_unit external_definition
	;

external_definition
	: function_definition
	| declaration
	;

function_definition
	: declaration_specifiers declarator declaration_list compound_statement
	{
		// Function
		struct VariableType returnType = {$1.vts, $1.pointerDegree};
		char* name = $2.name;

		AddFunctionSymbol(globalSymbolTable, $2.name, currentFunction);
		++functionTable->numFunctions;

		ALLOC_FUNCTION(functionTable);

		currentFunction = functionTable->functions[functionTable->numFunctions];
	}
	| declaration_specifiers declarator compound_statement
	{
		struct VariableType* p;
		struct Symbol* functionSymbol;

		// Function
		currentFunction->returnType.specifier = $1.vts;
		currentFunction->returnType.pointerDegree = $1.pointerDegree;
		currentFunction->body = PSUB_PARSENODE($3.pn, struct CompoundStatementParseNode);
		memcpy(currentFunction->parametersStart, $2.parametersDefinitions, $2.numParameters * sizeof(struct VariableType));
		currentFunction->parametersEnd += $2.numParameters;

		currentFunction->sizeOfParameters = 0;

		for (p = currentFunction->parametersStart; p < currentFunction->parametersEnd; ++p)
			currentFunction->sizeOfParameters += variableTypeSizes[p->specifier];

		functionSymbol = AddFunctionSymbol(globalSymbolTable, $2.name, currentFunction);
		currentFunction->name = functionSymbol->name;
		++functionTable->numFunctions;

		ALLOC_FUNCTION(functionTable);

		currentFunction = functionTable->functions[functionTable->numFunctions];
	}
	;

declaration_list
	: declaration
	| declaration_list declaration
	;

primary_expression
	: IDENTIFIER
	{
		PN_ALLOC($$.pn, struct IdentifierParseNode);
		PN_SET_TYPE($$.pn, PNT_IDENTIFIER);
		strcpy(PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->name, $1.name);
		PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);
	}
	| INT_CONST
	{
		PN_ALLOC($$.pn, struct ConstParseNode);
		PN_SET_TYPE($$.pn, PNT_CONST);
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.i32 = $1.s;
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.type = CT_INT;
	}
	| FLOAT_CONST
	{
		PN_ALLOC($$.pn, struct ConstParseNode);
		PN_SET_TYPE($$.pn, PNT_CONST);
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.f32 = $1.f;
		PSUB_PARSENODE($$.pn, struct ConstParseNode)->value.type = CT_FLOAT;
	}
	| STRING_LITERAL
	| '(' expression ')'
	{
		$$.pn = $2.pn;
	}
	;

postfix_expression
	: primary_expression
	{
		char* name = $1.name;
		struct IdentifierParseNode* ips = PSUB_PARSENODE($1.pn, struct IdentifierParseNode);
		char* name2 = ips->name;
		UInt breakHere = 0xABCDEFAB;
	}
	| postfix_expression '[' expression ']'
	| postfix_expression '(' ')'
	{
		// Function call
		PN_ALLOC($$.pn, struct CallParseNode);
		PN_SET_TYPE($$.pn, PNT_CALL);
		PSUB_PARSENODE($$.pn, struct CallParseNode)->function = PSUB_PARSENODE($1.pn, struct IdentifierParseNode);
		PSUB_PARSENODE($$.pn, struct CallParseNode)->parametersEnd = PSUB_PARSENODE($$.pn, struct CallParseNode)->parametersStart;
	}
	| postfix_expression '(' argument_expression_list ')'
	{
		char* name = $1.name;
		struct IdentifierParseNode* ips = PSUB_PARSENODE($1.pn, struct IdentifierParseNode);

		// Function call
		PN_ALLOC($$.pn, struct CallParseNode);
		PN_SET_TYPE($$.pn, PNT_CALL);
		PSUB_PARSENODE($$.pn, struct CallParseNode)->function = PSUB_PARSENODE($1.pn, struct IdentifierParseNode);
		memcpy(PSUB_PARSENODE($$.pn, struct CallParseNode)->parametersStart, $3.parameters, $3.numParameters * sizeof(struct ParseNode*));
		PSUB_PARSENODE($$.pn, struct CallParseNode)->parametersEnd = PSUB_PARSENODE($$.pn, struct CallParseNode)->parametersStart + $3.numParameters;
	}
	| postfix_expression '.' IDENTIFIER
	| postfix_expression PTR_OP IDENTIFIER
	{
		PN_ALLOC($$.pn, struct IdentifierParseNode);
		PN_SET_TYPE($$.pn, PNT_IDENTIFIER);
		PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);
		strcpy($$.name, $1.name);
	}
	| postfix_expression INC_OP
	| postfix_expression DEC_OP
	| '(' type_name ')' '{' initializer_list '}'
	| '(' type_name ')' '{' initializer_list ',' '}'
	;

argument_expression_list
	: assignment_expression
	{
		// Add function call parameter
		ADD_FUNCTION_CALL_PARAMETER($$, $1.pn);
	}
	| argument_expression_list ',' assignment_expression
	{
		// Add function call parameter
		ADD_FUNCTION_CALL_PARAMETER($$, $3.pn);
	}
	;

unary_expression
	: postfix_expression
	| INC_OP unary_expression
	| DEC_OP unary_expression
	| unary_operator cast_expression
	| SIZEOF unary_expression
	| SIZEOF '(' type_name ')'
	;

unary_operator : '&' | '*' | '+' | '-' | '~' | '!'
	;

cast_expression
	: unary_expression
	| '(' type_name ')' cast_expression
	;

multiplicative_expression
	: cast_expression
	| multiplicative_expression '*' cast_expression
	{
		PN_ALLOC($$.pn, struct MulParseNode);
		PN_SET_TYPE($$.pn, PNT_MUL);
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg1 = $3.pn;
	}
	| multiplicative_expression '/' cast_expression
	{
		PN_ALLOC($$.pn, struct DivParseNode);
		PN_SET_TYPE($$.pn, PNT_DIV);
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg1 = $3.pn;
	}
	| multiplicative_expression '%' cast_expression
	{
		PN_ALLOC($$.pn, struct DivParseNode);
		PN_SET_TYPE($$.pn, PNT_DIV);
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulParseNode)->arg1 = $3.pn;
	}
	;

additive_expression
	: multiplicative_expression
	| additive_expression '+' multiplicative_expression
	{
		PN_ALLOC($$.pn, struct AddParseNode);
		PN_SET_TYPE($$.pn, PNT_ADD);
		PSUB_PARSENODE($$.pn, struct AddParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AddParseNode)->arg1 = $3.pn;
	}
	| additive_expression '-' multiplicative_expression
	{
		PN_ALLOC($$.pn, struct SubParseNode);
		PN_SET_TYPE($$.pn, PNT_SUB);
		PSUB_PARSENODE($$.pn, struct SubParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct SubParseNode)->arg1 = $3.pn;
	}
	;

shift_expression
	: additive_expression
	| shift_expression LEFT_OP additive_expression
	| shift_expression RIGHT_OP additive_expression
	;

relational_expression
	: shift_expression
	| relational_expression '<' shift_expression
	{
		PN_ALLOC($$.pn, struct EqualityParseNode);
		PN_SET_TYPE($$.pn, PNT_EQUALITY);
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->eqType = ET_L;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg1 = $3.pn;
	}
	| relational_expression '>' shift_expression
	{
		PN_ALLOC($$.pn, struct EqualityParseNode);
		PN_SET_TYPE($$.pn, PNT_EQUALITY);
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->eqType = ET_G;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg1 = $3.pn;
	}
	| relational_expression LE_OP shift_expression
	{
		PN_ALLOC($$.pn, struct EqualityParseNode);
		PN_SET_TYPE($$.pn, PNT_EQUALITY);
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->eqType = ET_LE;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg1 = $3.pn;
	}
	| relational_expression GE_OP shift_expression
	{
		PN_ALLOC($$.pn, struct EqualityParseNode);
		PN_SET_TYPE($$.pn, PNT_EQUALITY);
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->eqType = ET_GE;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg1 = $3.pn;
	}
	;

equality_expression
	: relational_expression
	| equality_expression EQ_OP relational_expression
	{
		PN_ALLOC($$.pn, struct EqualityParseNode);
		PN_SET_TYPE($$.pn, PNT_EQUALITY);
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->eqType = ET_E;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct EqualityParseNode)->arg1 = $3.pn;
	}
	| equality_expression NE_OP relational_expression
	;

and_expression
	: equality_expression
	| and_expression '&' equality_expression
	;

exclusive_or_expression
	: and_expression
	| exclusive_or_expression '^' and_expression
	;

inclusive_or_expression
	: exclusive_or_expression
	| inclusive_or_expression '|' exclusive_or_expression
	;

logical_and_expression
	: inclusive_or_expression
	| logical_and_expression AND_OP inclusive_or_expression
	;

logical_or_expression
	: logical_and_expression
	| logical_or_expression OR_OP logical_and_expression
	;

conditional_expression
	: logical_or_expression
	| logical_or_expression '?' expression ':' conditional_expression
	;

assignment_expression
	: conditional_expression
	| unary_expression '=' assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);

		PN_ALLOC($$.pn, struct AssignParseNode);
		PN_SET_TYPE($$.pn, PNT_ASSIGN);
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg1 = $3.pn;
	}
	| unary_expression ADD_ASSIGN assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);

		PN_ALLOC($$.pn, struct AddAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_ADD_ASSIGN);
		PSUB_PARSENODE($$.pn, struct AddAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AddAssignParseNode)->arg1 = $3.pn;
	}
	| unary_expression SUB_ASSIGN assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);

		PN_ALLOC($$.pn, struct SubAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_SUB_ASSIGN);
		PSUB_PARSENODE($$.pn, struct SubAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct SubAssignParseNode)->arg1 = $3.pn;
	}
	| unary_expression MUL_ASSIGN assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);

		PN_ALLOC($$.pn, struct MulAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_MUL_ASSIGN);
		PSUB_PARSENODE($$.pn, struct MulAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct MulAssignParseNode)->arg1 = $3.pn;
	}
	| unary_expression DIV_ASSIGN assignment_expression
	{
		if (!PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier)
			PSUB_PARSENODE($1.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);

		PN_ALLOC($$.pn, struct DivAssignParseNode);
		PN_SET_TYPE($$.pn, PNT_DIV_ASSIGN);
		PSUB_PARSENODE($$.pn, struct DivAssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct DivAssignParseNode)->arg1 = $3.pn;
	}
	| unary_expression MOD_ASSIGN assignment_expression
	| unary_expression LEFT_ASSIGN assignment_expression
	| unary_expression RIGHT_ASSIGN assignment_expression
	| unary_expression AND_ASSIGN assignment_expression
	| unary_expression XOR_ASSIGN assignment_expression
	| unary_expression OR_ASSIGN assignment_expression
	;

expression
	: assignment_expression
	| expression ',' assignment_expression
	;

constant_expression
	: conditional_expression
	;

declaration
	: declaration_specifiers ';'
	{
		// Should never go here...
		ASSERT(0);
	}
	| declaration_specifiers init_declarator ';'
	{
		struct IdentifierParseNode* ipn;
		struct VariableType vt = {$1.vts, 0};

		if ($2.b)
			ipn = PSUB_PARSENODE(PSUB_PARSENODE($2.pn, struct AssignParseNode)->arg0, struct IdentifierParseNode);
		else
			ipn = PSUB_PARSENODE($2.pn, struct IdentifierParseNode);

		if (!ipn->identifier)
		{
			struct Variable* v = AddLocalVariable(vt.specifier, vt.pointerDegree, currentFunction);
			ipn->identifier = AddVariableSymbol(currentFunction, ipn->name, v);
		}

		$$.pn = $2.pn;
	}
	;

declaration_specifiers
	: storage_class_specifier
	| storage_class_specifier declaration_specifiers
	| type_specifier
	| type_specifier declaration_specifiers
	| type_qualifier
	| type_qualifier declaration_specifiers
	| function_specifier
	| function_specifier declaration_specifiers
	;

init_declarator
	: declarator
	{
		$$.pn = $1.pn;
		$$.b = FALSE;
	}
	| declarator '=' initializer
	{
		// Here we are sending "up" the id parse node without a variable symbol & variable. The upper creates that.
		PN_ALLOC($$.pn, struct AssignParseNode);
		PN_SET_TYPE($$.pn, PNT_ASSIGN);
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg0 = $1.pn;
		PSUB_PARSENODE($$.pn, struct AssignParseNode)->arg1 = $3.pn;

		$$.b = TRUE;
	}
	;

storage_class_specifier
	: TYPEDEF
	| STATIC
	;

type_specifier
	: VOID     { $$.vts = VTS_VOID; }
	| INT8     { $$.vts = VTS_INT8; }
	| INT16    { $$.vts = VTS_INT16; }
	| INT32    { $$.vts = VTS_INT32; }
	| INT      { $$.vts = VTS_INT; }
	| UINT     { $$.vts = VTS_UINT; }
	| FLOAT32  { $$.vts = VTS_FLOAT32; }
	| FLOAT64  { $$.vts = VTS_FLOAT64; }
	| FLOAT    { $$.vts = VTS_FLOAT; }
	| BOOL_TKN { $$.vts = VTS_UINT; }
	| struct_or_union_specifier
	| enum_specifier
	| TYPE_NAME
	;

struct_or_union_specifier
	: struct_or_union IDENTIFIER '{' struct_declaration_list '}'
	| struct_or_union '{' struct_declaration_list '}'
	| struct_or_union IDENTIFIER
	;

struct_or_union
	: CLASS
	| UNION
	;

struct_declaration_list
	: struct_declaration
	| struct_declaration_list struct_declaration
	;

struct_declaration
	: specifier_qualifier_list struct_declarator_list ';'
	;

specifier_qualifier_list
	: type_specifier specifier_qualifier_list
	| type_specifier
	| type_qualifier specifier_qualifier_list
	| type_qualifier
	;

struct_declarator_list
	: struct_declarator
	| struct_declarator_list ',' struct_declarator
	;

struct_declarator
	: declarator
	| ':' constant_expression
	| declarator ':' constant_expression
	;

enum_specifier
	: ENUM '{' enumerator_list '}'
	| ENUM IDENTIFIER '{' enumerator_list '}'
	| ENUM '{' enumerator_list ',' '}'
	| ENUM IDENTIFIER '{' enumerator_list ',' '}'
	| ENUM IDENTIFIER
	;

enumerator_list
	: enumerator
	| enumerator_list ',' enumerator
	;

enumerator
	: IDENTIFIER
	| IDENTIFIER '=' constant_expression
	;

type_qualifier
	: CONST
	| VOLATILE
	;

function_specifier
	: INLINE
	;

declarator
	: pointer direct_declarator
	{
		$$.pointerDegree = $1.pointerDegree;
		strcpy($$.name, $2.name);
	}
	| direct_declarator
	;


direct_declarator
	: IDENTIFIER
	{
		PN_ALLOC($$.pn, struct IdentifierParseNode);
		PN_SET_TYPE($$.pn, PNT_IDENTIFIER);
		PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);
		strcpy(PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->name, $1.name);
	}
	| '(' declarator ')'
	| direct_declarator '[' type_qualifier_list assignment_expression ']'
	| direct_declarator '[' type_qualifier_list ']'
	| direct_declarator '[' assignment_expression ']'
	| direct_declarator '[' STATIC type_qualifier_list assignment_expression ']'
	| direct_declarator '[' type_qualifier_list STATIC assignment_expression ']'
	| direct_declarator '[' type_qualifier_list '*' ']'
	| direct_declarator '[' '*' ']'
	| direct_declarator '[' ']'
	| direct_declarator '(' parameter_type_list ')'
	{
		// Function definition, has parameters. identifier is $1
		UInt i;

		strcpy($$.name, $1.name);
		COPY_C_ARRAY($$.parametersDefinitions, $$.numParameters,
			         $3.parametersDefinitions, $3.numParameters,
					 sizeof(struct VariableType));

		COPY_C_ARRAY($$.parametersDefinitionsNames, $$.numParameters,
			         $3.parametersDefinitionsNames, $3.numParameters,
					 IDENTIFIER_NAME_MAX_LEN);

		COPY_C_ARRAY($$.parameters, $$.numParameters,
			         $3.parameters, $3.numParameters,
					 sizeof(struct ParseNode*));

		for (i = 0; i < $$.numParameters; ++i)
		{
			struct Variable* v;

			v = AddLocalParameter($$.parametersDefinitions[i].specifier, $$.parametersDefinitions[i].pointerDegree, currentFunction);
			AddVariableSymbol(currentFunction, $$.parametersDefinitionsNames[i], v);
		}
	}
	| direct_declarator '(' identifier_list ')'
	{
		// Function definition
		char* name = $1.name;
		UInt breakHere = 0xDEADFACE;
	}
	| direct_declarator '(' ')'
	{
		// Function definition
		char* name = $1.name;
		UInt breakHere = 0xDEADFACE;
	}
	;

pointer
	: '*'
	{
		++$$.pointerDegree;
	}
	| '*' type_qualifier_list
	{
		++$$.pointerDegree;
	}
	| '*' pointer
	{
		$$.pointerDegree = $2.pointerDegree + 1;
	}
	| '*' type_qualifier_list pointer
	{
		++$$.pointerDegree;
	}
	;

type_qualifier_list
	: type_qualifier
	| type_qualifier_list type_qualifier
	;	


parameter_type_list
	: parameter_list
	| parameter_list ',' ELLIPSIS
	;

parameter_list
	: parameter_declaration
	{
		// Add parameter
		COPY_C_ARRAY($$.parametersDefinitions, $$.numParameters,
			         $1.parametersDefinitions, $1.numParameters,
					 sizeof(struct VariableType));
		memcpy($$.parametersDefinitionsNames, $1.parametersDefinitionsNames, $1.numParameters * IDENTIFIER_NAME_MAX_LEN);

		ADD_FUNCTION_DEFINITION_PARAMETER($$, $1.vts, $1.pointerDegree, $1.name, $1.pn);
 	}
	| parameter_list ',' parameter_declaration
	{
		// Add parameter
		COPY_C_ARRAY($$.parametersDefinitions, $$.numParameters,
			         $1.parametersDefinitions, $1.numParameters,
					 sizeof(struct VariableType));
		memcpy($$.parametersDefinitionsNames, $1.parametersDefinitionsNames, $1.numParameters * IDENTIFIER_NAME_MAX_LEN);

		ADD_FUNCTION_DEFINITION_PARAMETER($$, $3.vts, $3.pointerDegree, $3.name, $3.pn);
	}
	;

parameter_declaration
	: declaration_specifiers declarator
	{
		$$.vts = $1.vts;
		$$.pointerDegree = $2.pointerDegree;
		$$.pn = $2.pn;
		strcpy($$.name, $2.name);
	}
	| declaration_specifiers abstract_declarator
	{
		$$.vts = $1.vts;
		$$.pointerDegree = $2.pointerDegree;
	}
	| declaration_specifiers
	{
		enum VARIABLE_TYPE_SPECIFIER vts = $1.vts;
		UInt pointerDegree = $1.pointerDegree;
		UInt breakHere = 0xDEADFACE;
	}
	;

identifier_list
	: IDENTIFIER
	{
		PN_ALLOC($$.pn, struct IdentifierParseNode);
		PN_SET_TYPE($$.pn, PNT_IDENTIFIER);
		PSUB_PARSENODE($$.pn, struct IdentifierParseNode)->identifier = FindIdentifierSymbol(globalSymbolTable, &currentFunction->symbolTable, $1.name);
		strcpy($$.name, $1.name);
	}
	| identifier_list ',' IDENTIFIER
	;

type_name
	: specifier_qualifier_list
	| specifier_qualifier_list abstract_declarator
	;

abstract_declarator
	: pointer
	| direct_abstract_declarator
	| pointer direct_abstract_declarator
	;

direct_abstract_declarator
	: '(' abstract_declarator ')'
	| '[' ']'
	| '[' assignment_expression ']'
	| direct_abstract_declarator '[' ']'
	| direct_abstract_declarator '[' assignment_expression ']'
	| '[' '*' ']'
	| direct_abstract_declarator '[' '*' ']'
	| '(' ')'
	| '(' parameter_type_list ')'
	| direct_abstract_declarator '(' ')'
	| direct_abstract_declarator '(' parameter_type_list ')'
	;

initializer
	: assignment_expression
	| '{' initializer_list '}'
	| '{' initializer_list ',' '}'
	;

initializer_list
	: initializer
	| designation initializer
	| initializer_list ',' initializer
	| initializer_list ',' designation initializer
	;

designation
	: designator_list '='
	;

designator_list
	: designator
	| designator_list designator
	;

designator
	: '[' constant_expression ']'
	| '.' IDENTIFIER
	;

statement
	: labeled_statement
	| compound_statement
	| expression_statement
	| selection_statement
	| iteration_statement
	| jump_statement
	;

labeled_statement
	: IDENTIFIER ':' statement
	| CASE constant_expression ':' statement
	| DEFAULT ':' statement
	;

compound_statement
	: '{' '}'
	{
		PN_ALLOC($$.pn, struct CompoundStatementParseNode);
		PN_SET_TYPE($$.pn, PNT_COMPOUND_STATEMENT);
		PSUB_PARSENODE($$.pn, struct CompoundStatementParseNode)->statements[0] = NULL;
	}
	| '{' block_item_list '}'
	{
		PN_ALLOC($$.pn, struct CompoundStatementParseNode);
		PN_SET_TYPE($$.pn, PNT_COMPOUND_STATEMENT);
		memcpy(PSUB_PARSENODE($$.pn, struct CompoundStatementParseNode)->statements,
			$2.compoundStatement, $2.numStatements * sizeof(struct ParseNode*));
		PSUB_PARSENODE($$.pn, struct CompoundStatementParseNode)->statements[$2.numStatements] = NULL;
	}
	;

block_item_list
	: block_item
	{
		ADD_TO_COMPOUND_STATEMENT($$, $1.pn);
	}
	| block_item_list block_item
	{
		ADD_TO_COMPOUND_STATEMENT($$, $2.pn);
	}
	;

block_item
	: declaration
	| statement
	;

expression_statement
	: ';'
	| expression ';'
	;

selection_statement
	: IF '(' expression ')' statement
	{
		PN_ALLOC($$.pn, struct IfParseNode);
		PN_SET_TYPE($$.pn, PNT_IF);
		PSUB_PARSENODE($$.pn, struct IfParseNode)->conditionalExpression = $3.pn;
		PSUB_PARSENODE($$.pn, struct IfParseNode)->bodyCompoundStatement = $5.pn;
	}
	| IF '(' expression ')' statement ELSE statement
	| SWITCH '(' expression ')' statement
	;

iteration_statement
	: WHILE '(' expression ')' statement
	{
		PN_ALLOC($$.pn, struct WhileParseNode);
		PN_SET_TYPE($$.pn, PNT_WHILE);
		PSUB_PARSENODE($$.pn, struct WhileParseNode)->conditionalExpression = $3.pn;
		PSUB_PARSENODE($$.pn, struct WhileParseNode)->bodyCompoundStatement = $5.pn;
	}
	| DO statement WHILE '(' expression ')' ';'
	| FOR '(' expression_statement expression_statement ')' statement
	{
		PN_ALLOC($$.pn, struct ForParseNode);
		PN_SET_TYPE($$.pn, PNT_FOR);
		PSUB_PARSENODE($$.pn, struct ForParseNode)->initStatement = $3.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->conditionalExpression = $4.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->countingStatement = NULL;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->bodyCompoundStatement = $6.pn;
	}
	| FOR '(' expression_statement expression_statement expression ')' statement
	{
		PN_ALLOC($$.pn, struct ForParseNode);
		PN_SET_TYPE($$.pn, PNT_FOR);
		PSUB_PARSENODE($$.pn, struct ForParseNode)->initStatement = $3.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->conditionalExpression = $4.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->countingStatement = $5.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->bodyCompoundStatement = $7.pn;
	}
	| FOR '(' declaration expression_statement ')' statement
	{
		PN_ALLOC($$.pn, struct ForParseNode);
		PN_SET_TYPE($$.pn, PNT_FOR);
		PSUB_PARSENODE($$.pn, struct ForParseNode)->initStatement = $3.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->conditionalExpression = $4.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->countingStatement = NULL;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->bodyCompoundStatement = $6.pn;
	}
	| FOR '(' declaration expression_statement expression ')' statement
	{
		PN_ALLOC($$.pn, struct ForParseNode);
		PN_SET_TYPE($$.pn, PNT_FOR);
		PSUB_PARSENODE($$.pn, struct ForParseNode)->initStatement = $3.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->conditionalExpression = $4.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->countingStatement = $5.pn;
		PSUB_PARSENODE($$.pn, struct ForParseNode)->bodyCompoundStatement = $7.pn;
	}
	;

jump_statement
	: GOTO IDENTIFIER ';'
	| CONTINUE ';'
	| BREAK ';'
	| RETURN ';'
	{
		PLAIN_PN_ALLOC($$.pn, PNT_RET_VOID);
	}
	| RETURN expression ';'
	{
		PN_ALLOC($$.pn, struct ReturnParseNode);
		PN_SET_TYPE($$.pn, PNT_RET);
		PSUB_PARSENODE($$.pn, struct ReturnParseNode)->returnValue = $2.pn;
	}
	;

%%

void yyerror(const char* s)
{
	fflush(stdout);
	COMPILER_ERRORF("r:%u.c:%u - %s", row, col, s);
}

static void PrintTokenValue(FILE* file, int type, YYSTYPE value)
{
	if (type == IDENTIFIER)
		fprintf(file, "%s", value.name);
	else if (type == INT_CONST)
		fprintf(file, "%d", value.s);
}

#include "lex.yy.c"